/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.edu.agh.student.vrp;

import org.jgap.*;
import org.jgap.event.EventManager;
import org.jgap.impl.*;

import java.io.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.apache.commons.configuration.PropertiesConfiguration;

/**
 * @author janburkot
 */
public class GA {

    private int maxAllowedEvolutions = 1000;
    private List<Destination> l;
    private int totalVehicles = 10 + 9;
    private int vehicleCapacity = 200;
    private int populationSize = 500;

    int mFactor = 25;
    int cFactor = 1;
    int bFactor = 40;
    int wFactor = 1;
    int sFactor = 2000000;

    int n = 25 + 1;

    private BufferedWriter outputWriter;

    public GA(){

    }

    public void init(PropertiesConfiguration config, File file) throws IOException {
        maxAllowedEvolutions = config.getInt("evolutions");
        totalVehicles = config.getInt("vehicles") - 1;
        vehicleCapacity = config.getInt("capacity");
        populationSize = config.getInt("population");

        cFactor = config.getInt("c");
        mFactor = config.getInt("m");
        bFactor = config.getInt("b");
        wFactor = config.getInt("w");
        sFactor = config.getInt("s");

        n = config.getInt("n") + 1;

        outputWriter = new BufferedWriter(new FileWriter(file));

        Configuration.reset();
    }

    private Configuration conf = new DefaultConfiguration();

    public void load(File file) throws Exception {
        BufferedReader reader = new BufferedReader(new FileReader(file));
        l = new LinkedList<Destination>();

        String line;
        int i =0;
        while (i< n && (line = reader.readLine()) != null) {
            line = line.trim();

            //System.out.println(line);
            //System.out.println(line.split(" +").length);
            String[] col = line.split(" +");

            Destination d = new Destination(Integer.parseInt(col[0]),
                    Double.parseDouble(col[1]),
                    Double.parseDouble(col[2]),
                    Double.parseDouble(col[3]),
                    Double.parseDouble(col[4]),
                    Double.parseDouble(col[5]),
                    Double.parseDouble(col[6]));

            l.add(d);
            i++;
        }
    }

    public void run() throws Exception {

        BestChromosomesSelector bestChromsSelector = new BestChromosomesSelector(conf, 1.0D);

        bestChromsSelector.setDoubletteChromosomesAllowed(false);
        conf.addNaturalSelector(bestChromsSelector, true);
        //conf.setRandomGenerator(new StockRandomGenerator());
        conf.setMinimumPopSizePercent(0);
        conf.setEventManager(new EventManager());
        //conf.setFitnessEvaluator(new DefaultFitnessEvaluator());
        conf.setChromosomePool(new ChromosomePool());

        conf.getGeneticOperators().clear();
        MyCrossover crossover = new MyCrossover(conf);
        crossover.setGa(this);
        //conf.addGeneticOperator(crossover);
        conf.addGeneticOperator(new PmxCrossover(conf));
        conf.addGeneticOperator(new SwappingMutationOperator(conf, 20));


        //
        FitnessFunction myFunc = new MyFitnessFunction(this);//new MinimizingMakeChangeFitnessFunction(targetAmount);

        conf.setFitnessFunction(myFunc);

        IChromosome sampleChromosome = createSampleChromosome(l.size() - 1, getTotalVehicles());
        conf.setSampleChromosome(sampleChromosome);

        conf.setPopulationSize(populationSize);

        InitialPopulationGenerator populationGen =
                new InitialPopulationGenerator(l, populationSize, totalVehicles, conf);
        IChromosome[] chromosomes = populationGen.getInitialPopulation();
        Genotype population = new Genotype(conf, new Population(conf, chromosomes));


//wypisuje chromosomy po zainicjalizowaniu       
//        for (IChromosome chromosome : chromosomes) {
//            pl.edu.agh.student.vrp.utils.DisplayUtils.displayChromosome(chromosome);
//        }


        IChromosome bestSolutionSoFar = population.getFittestChromosome();

        for (int i = 0; i < 1 * maxAllowedEvolutions; i++) {
            population.evolve();
//wypisywanie fitness co 20
//            if ((i % 20) == 0) {
//                System.out.println(i + ") F " + population.getFittestChromosome().getFitnessValue());
//                System.out.println(population.getFittestChromosome().getFitnessValue() - 1073741823.D);
//            }
        }

        bestSolutionSoFar = population.getFittestChromosome();

//        int[] tab = new int[l.size()];
//
//        int i = 1;
//        for (Gene g : bestSolutionSoFar.getGenes()) {
//            IntegerGene ig = (IntegerGene) g;
//            System.out.println(i + ") " + ig.intValue());
//            i++;
//            tab[ig.intValue()]++;
//        }

//        for (int a = 0; a < tab.length; a++) {
//            System.out.println(a + ") " + tab[a]);
//        }
//
//        System.out.println(myFunc.getFitnessValue(bestSolutionSoFar) - 107374182300.D);
        PrintUtils x = new PrintUtils(this);
        x.print(bestSolutionSoFar, outputWriter);
    }

    public IChromosome createSampleChromosome(int c, int v) {
        try {
            Gene[] genes = new Gene[c + v];
            for (int i = 0; i < c; ++i) {
                genes[i] = new IntegerGene(conf, 1, c);
                genes[i].setAllele(new Integer(i + 1));
            }

            for (int i = 0; i < v; ++i) {
                genes[c + i] = new IntegerGene(conf, 0, 0);
                genes[c + i].setAllele(new Integer(0));
            }
            IChromosome sample = new Chromosome(conf, genes);

            return sample;
        } catch (InvalidConfigurationException iex) {
            throw new IllegalStateException(iex.getMessage());
        }
    }

    protected int[] V(int n) {
        List<Integer> x = new LinkedList<Integer>();
        for (int i = 0; i < n; i++) {
            x.add(i);
        }

        int[] y = new int[n];
        Random rand = new Random();
        for (int i = 0; i < n; i++) {
            ;
            int r = rand.nextInt(x.size());
            int idx = x.remove(r);
            y[i] = idx;
        }
        return y;
    }

    public List<Destination> getDestinations() {
        return l;
    }

    public int getPopulationSize() {
        return populationSize;
    }

    public int getTotalVehicles() {
        return totalVehicles;
    }

    public int getVehicleCapacity() {
        return vehicleCapacity;
    }

    public void close() throws IOException {
        outputWriter.close();
    }
}
